package com.moxiao.yfjh.module.activity.service.activityInfo;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.moxiao.yfjh.framework.common.pojo.PageResult;
import com.moxiao.yfjh.framework.common.util.date.DateUtils;
import com.moxiao.yfjh.module.activity.common.ActivityNotifyMessageUtils;
import com.moxiao.yfjh.module.activity.common.NoCommonUtil;
import com.moxiao.yfjh.module.activity.controller.admin.activityEnroll.vo.ActivityEnrollExportReqVO;
import com.moxiao.yfjh.module.activity.controller.admin.activityEnroll.vo.ActivityEnrollRespVO;
import com.moxiao.yfjh.module.activity.controller.admin.activityInfo.vo.ActivityInfoCreateReqVO;
import com.moxiao.yfjh.module.activity.controller.admin.activityInfo.vo.ActivityInfoExportReqVO;
import com.moxiao.yfjh.module.activity.controller.admin.activityInfo.vo.ActivityInfoPageReqVO;
import com.moxiao.yfjh.module.activity.controller.admin.activityInfo.vo.ActivityInfoUpdateReqVO;
import com.moxiao.yfjh.module.activity.controller.admin.activityPackage.vo.ActivityPackageCreateReqVO;
import com.moxiao.yfjh.module.activity.convert.activityInfo.ActivityInfoConvert;
import com.moxiao.yfjh.module.activity.dal.dataobject.activityEnroll.ActivityEnrollDO;
import com.moxiao.yfjh.module.activity.dal.dataobject.activityInfo.ActivityInfoDO;
import com.moxiao.yfjh.module.activity.dal.mysql.activityInfo.ActivityInfoMapper;
import com.moxiao.yfjh.module.activity.service.activityEnroll.ActivityEnrollService;
import com.moxiao.yfjh.module.activity.service.activityPackage.ActivityPackageService;
import com.moxiao.yfjh.module.pay.api.refund.dto.PayRefundCreateReqDTO;
import com.moxiao.yfjh.module.pay.dal.dataobject.app.PayAppDO;
import com.moxiao.yfjh.module.pay.dal.dataobject.order.PayOrderDO;
import com.moxiao.yfjh.module.pay.service.app.PayAppService;
import com.moxiao.yfjh.module.pay.service.order.PayOrderService;
import com.moxiao.yfjh.module.pay.service.refund.PayRefundService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static com.moxiao.yfjh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.moxiao.yfjh.module.activity.enums.ErrorCodeConstants.INFO_NOT_EXISTS;

/**
 * 相亲活动信息 Service 实现类
 *
 * @author mx
 */
@Service
@Validated
@Log4j2
public class ActivityInfoServiceImpl implements ActivityInfoService {

    @Resource
    private ActivityInfoMapper infoMapper;

    public static final String ONE_STR = "1";
    @Resource
    private ActivityEnrollService enrollService;
    @Resource
    private ActivityNotifyMessageUtils messageUtils;
    @Resource
    private PayOrderService payOrderService;
    @Resource
    private PayAppService payAppService;
    @Resource
    private PayRefundService payRefundService;
    @Resource
    private ActivityPackageService packageService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createInfo(ActivityInfoCreateReqVO createReqVO) {
        // 插入
        ActivityInfoDO info = ActivityInfoConvert.INSTANCE.convert(createReqVO);
        infoMapper.insert(info);
        // 返回
        return info.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCommonInfo(ActivityInfoCreateReqVO createReqVO) {
        // 创建活动
        Long id = createInfo(createReqVO);
        if (Objects.nonNull(id)) {
            List<ActivityPackageCreateReqVO> packageProductRespVOList = createReqVO.getPackageProductRespVOList();
            if (CollectionUtils.isNotEmpty(packageProductRespVOList)) {
                packageProductRespVOList.forEach(activityPackageCreateReqVO -> {
                    activityPackageCreateReqVO.setActiveId(id);
                });
                packageService.createPackage(packageProductRespVOList);
            }
        }
        return id;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(ActivityInfoUpdateReqVO updateReqVO) {
        // 校验存在
        validateInfoExists(updateReqVO.getId());
        // 更新
        ActivityInfoDO updateObj = ActivityInfoConvert.INSTANCE.convert(updateReqVO);
        infoMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer updateByInfo(@Valid ActivityInfoUpdateReqVO updateReqVO) {
        // 校验存在
        validateInfoExists(updateReqVO.getId());
        // 更新
        ActivityInfoDO updateObj = ActivityInfoConvert.INSTANCE.convert(updateReqVO);

        return infoMapper.update(null, new LambdaUpdateWrapper<ActivityInfoDO>()
                .set(ActivityInfoDO::getStatus, updateObj.getStatus())
                .set(ActivityInfoDO::getRejectReason, updateObj.getRejectReason())
                .set(ActivityInfoDO::getAuditStatus, updateObj.getAuditStatus())
                .set(ActivityInfoDO::getAuditUser, updateObj.getAuditUser())
                .set(ActivityInfoDO::getAuditTime, updateObj.getAuditTime())
                .eq(ActivityInfoDO::getId, updateObj.getId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfo(Long id) {
        // 校验存在
        validateInfoExists(id);
        // 删除
        infoMapper.deleteById(id);
    }

    private void validateInfoExists(Long id) {
        if (infoMapper.selectById(id) == null) {
            throw exception(INFO_NOT_EXISTS);
        }
    }

    @Override
    public ActivityInfoDO getInfo(Long id) {
        return infoMapper.selectById(id);
    }

    @Override
    public List<ActivityInfoDO> getInfoList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ListUtil.empty();
        }
        return infoMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<ActivityInfoDO> getInfoPage(ActivityInfoPageReqVO pageReqVO) {
        return infoMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<ActivityInfoDO> getAppInfoPage(ActivityInfoPageReqVO pageReqVO) {
        return infoMapper.selectAppPage(pageReqVO);
    }

    @Override
    public PageResult<ActivityInfoDO> selectPageByHeaderId(ActivityInfoPageReqVO pageReqVO) {
        return infoMapper.selectPageByHeaderId(pageReqVO);
    }

    @Override
    public List<ActivityInfoDO> getInfoPageByNotHeader(ActivityInfoPageReqVO pageReqVO) {
        if (pageReqVO.getIsPage() != 0) {
            pageReqVO.setPageNo((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize());
        }
        return infoMapper.selectPageByNotHeader(pageReqVO);
    }

    @Override
    public Integer getInfoActivityInfoNotHeaderSum(ActivityInfoPageReqVO pageReqVO) {
        return infoMapper.getInfoActivityInfoNotHeaderSum(pageReqVO);
    }

    @Override
    public List<ActivityInfoDO> getInfoList(ActivityInfoExportReqVO exportReqVO) {
        return infoMapper.selectList(exportReqVO);
    }

    @Override
    public ActivityInfoDO getActivityTodayLast(Date todayStart, Date todayEnd) {
        LambdaQueryWrapper<ActivityInfoDO> lqw = Wrappers.lambdaQuery();
        if (Objects.nonNull(todayStart) && Objects.nonNull(todayEnd)) {
            lqw.between(ActivityInfoDO::getCreateTime, todayStart, todayEnd);
        }
        lqw.orderByDesc(ActivityInfoDO::getCreateTime);
        lqw.last("LIMIT " + 1);
        return infoMapper.selectOne(lqw);
    }

    @Override
    public Integer getActiveNo(String activeNo) {
        return infoMapper.getActiveNo(activeNo);
    }

    @Override
    public ActivityInfoDO getActivityInfo(String activeNo) {
        return infoMapper.selectOne(ActivityInfoDO::getNo, activeNo);
    }

    @Override
    public ActivityInfoDO getNo(String activeNo) {
        return infoMapper.getNo(activeNo);
    }

    @Override
    public int getBlindUserSex(Long userId) {
        return infoMapper.getBlindUserSex(userId);
    }

    @Override
    public BigDecimal getSupplypriceAmount(Long activityId) {
        return infoMapper.getSupplypriceAmount(activityId);
    }

    @Override
    public List<ActivityInfoDO> getInfoPageList(ActivityInfoPageReqVO pageReqVO) {
        if (pageReqVO.getIsPage() != 0) {
            pageReqVO.setPageNo((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize());
        }
        return infoMapper.selectPageList(pageReqVO);
    }

    /**
     *  活动 定时任务
     */
//    @Scheduled(cron = "0 30 23 * * ?") // 每天晚上23点50分执行
    @Scheduled(cron = "0 */5 * * * ?") // 每分钟执行一次
    public void performTaskActivity() {
        List<ActivityInfoDO> list = infoMapper.getTaskActivityList();
        log.info("【{}】-活动轮询，当前活动数量：{}", DateUtils.getDayString(), list.size());
        if (CollectionUtils.isNotEmpty(list)) {
            // 对比活动截止时间
            list.forEach(info -> {
                if (StringUtils.isNotBlank(info.getDeadline())) {
                    int type = NoCommonUtil.getCompareTo(info.getDeadline());
                    if (type == 1) {
                        getSelf().taskActivity(info);
                    }
                }
            });
        }
        log.info("【{}】-活动状态执行结束", DateUtils.getDayString());
    }

    /**
     * 定时任务：处理活动及报名
     * 注意，如果是方法内调用该方法，需要通过 getSelf() 调用，否则事务不生效
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskActivity(ActivityInfoDO info) {
        /**
         * TODO 写在未处理前，先获得活动的已报名人数和选中的组队人数，从而验证活动是否缺少人数
         */
        log.info("【{}】-开始处理-{}-的活动相关业务", DateUtils.getDayString(), info.getName());
        //1 男生 获得已报名
        int registeredMenNum = enrollService.getNumber(info.getId(), (byte) 1, (byte) 1);
        //2 女生 获得已报名
        int registeredWomenNum = enrollService.getNumber(info.getId(), (byte) 2, (byte) 1);
        //3 男生 已参加活动人数
        int a_MenNum = enrollService.getNumber(info.getId(), (byte) 1, (byte) 0);
        //4 女生 已参加活动人数
        int a_WomenNum = enrollService.getNumber(info.getId(), (byte) 2, (byte) 0);
        //5 活动人数
        Integer num = Integer.parseInt(info.getWomenNum()) + Integer.parseInt(info.getMenNum());
        // 合计：已参加活动人数
        Integer enroll_num = a_MenNum + a_WomenNum;
        // 比较指定日期时间和当前系统时间
        // 人数达标，活动开始
        ActivityInfoUpdateReqVO updateReqVO = new ActivityInfoUpdateReqVO();
        updateReqVO.setId(info.getId());
        // 人数够的，活动开始
        if (enroll_num.equals(num)) {
            //5.1 已报名的活动处理
            if (Objects.equals(info.getStatus(), (byte) 2)) {
                updateReqVO.setStatus((byte) 3);
            } else {
                updateReqVO.setStatus((byte) 4);
            }
            // 修改活动状态
            infoMapper.update(null, new LambdaUpdateWrapper<ActivityInfoDO>()
                    .set(ActivityInfoDO::getStatus, updateReqVO.getStatus())
                    .eq(ActivityInfoDO::getId, updateReqVO.getId()));
        } else {
            // 官方活动
            if (ONE_STR.equals(info.getActiveSource())) {
                updateReqVO.setStatus((byte) 8);
                // 修改活动状态
                infoMapper.update(null, new LambdaUpdateWrapper<ActivityInfoDO>()
                        .set(ActivityInfoDO::getStatus, updateReqVO.getStatus())
                        .eq(ActivityInfoDO::getId, updateReqVO.getId()));
            } else {
                // 5.2 活动报名，强制匹配
                if (registeredMenNum > 0) {
                    // 计算缺少的人数
                    int u_num = Integer.parseInt(info.getMenNum()) - a_MenNum;
                    if (u_num > 0) {
                        //5.3 报名人数 等于活动缺少的人数，或者大于时，男生活动人数足够
                        if (registeredMenNum == u_num || registeredMenNum > u_num) {
                            enrollService.updateActivityStatus(info.getId(), (byte) 1, u_num);
                        } else {
                            updateReqVO.setStatus((byte) 7); // 活动人数不足。活动关闭
                        }
                    }
                } else {
                    updateReqVO.setStatus((byte) 7);// 活动人数不足。活动关闭
                }
                if (registeredWomenNum > 0) {
                    // 计算缺少的人数
                    int u_num = Integer.parseInt(info.getWomenNum()) - a_WomenNum;
                    if (u_num > 0) {
                        //6 报名人数 等于活动缺少的人数，或者大于时，女生活动人数足够
                        if (registeredWomenNum == u_num || registeredWomenNum > u_num) {
                            // 根据报名时间来对已报名的人更新到已选中参加活动
                            enrollService.updateActivityStatus(info.getId(), (byte) 2, u_num);
                        }
                    } else {
                        updateReqVO.setStatus((byte) 7);// 活动人数不足。活动关闭
                    }
                } else {
                    updateReqVO.setStatus((byte) 7);// 活动人数不足。活动关闭
                }
                //7 处理完成后，验证活动人数是否达标
                int y_MenNum = enrollService.getNumber(info.getId(), (byte) 1, (byte) 0);
                int y_WomenNum = enrollService.getNumber(info.getId(), (byte) 2, (byte) 0);
                Integer info_num = y_MenNum + y_WomenNum;
                if (info_num.equals(num)) {
                    updateReqVO.setStatus((byte) 3);
                } else {
                    updateReqVO.setStatus((byte) 7);
                }
                // 修改活动状态
                infoMapper.update(null, new LambdaUpdateWrapper<ActivityInfoDO>()
                        .set(ActivityInfoDO::getStatus, updateReqVO.getStatus())
                        .eq(ActivityInfoDO::getId, info.getId()));
                //8 活动状态是未开始的时候，对为选中的报名人员进行退费操作
                if (Objects.equals(updateReqVO.getStatus(), 3)) {
                    ActivityEnrollExportReqVO exportReqVO = new ActivityEnrollExportReqVO();
                    exportReqVO.setActivityId(info.getId());
                    exportReqVO.setActivityStatus((byte) 1);
                    exportReqVO.setStatus((byte) 0);
                    List<ActivityEnrollDO> enrollDOList = enrollService.getEnrollList(exportReqVO);
                    if (!enrollDOList.isEmpty()) {
                        enrollDOList.forEach(enroll -> {
                            ActivityEnrollRespVO enrollRespVO = new ActivityEnrollRespVO();
                            enrollRespVO.setActivityName(info.getName());
                            enrollRespVO.setActivityId(info.getId());
                            enrollRespVO.setActiveDate(info.getActiveDate());//  + " " + item.getActiveTimeBegin()
                            int type = -1;
                            if (Objects.equals(enroll.getUserId(), info.getHeadUserId())) {
                                enrollRespVO.setUserId(info.getHeadUserId());
                                type = 1;
                            } else {
                                type = 2;
                                enrollRespVO.setUserId(enroll.getUserId());
                            }
                            // 更新报名状态
                            enrollService.updatePayStatus(enrollRespVO.getId(), (byte) 2);
                            // 进行报名退款
                            createPayRefund(enrollRespVO);
                        });
                    }
                } else {
                    ActivityEnrollExportReqVO exportReqVO = new ActivityEnrollExportReqVO();
                    exportReqVO.setActivityId(info.getId());
                    exportReqVO.setStatus((byte) 0);
                    List<ActivityEnrollDO> enrollDOList = enrollService.getEnrollList(exportReqVO);
                    if (!enrollDOList.isEmpty()) {
                        enrollDOList.forEach(enroll -> {
                            ActivityEnrollRespVO enrollRespVO = new ActivityEnrollRespVO();
                            enrollRespVO.setActivityName(info.getName());
                            enrollRespVO.setActivityId(info.getId());
                            enrollRespVO.setActiveDate(info.getActiveDate());//  + " " + item.getActiveTimeBegin()
                            int type = -1;
                            if (Objects.equals(enroll.getUserId(), info.getHeadUserId())) {
                                enrollRespVO.setUserId(info.getHeadUserId());
                                type = 1;
                            } else {
                                type = 2;
                                enrollRespVO.setUserId(enroll.getUserId());
                            }
                            // 根据type来确定发送的通知类型
                            messageUtils.createActivityCreateCleanMessage(enrollRespVO, type);
                            // 更新报名状态
                            enrollService.updatePayStatus(enrollRespVO.getId(), (byte) 2);
                            // 进行报名退款
                            createPayRefund(enrollRespVO);
                        });
                    }
                }
            }
        }
    }


    /**
     *  活动通知任务 定时任务
     */
    @Scheduled(cron = "0 0 10 * * ?") // 每天10点执行
//    @Scheduled(cron = "1 * * * * ?") // 每分钟执行一次
    public void messageTaskActivity() {
        log.info("【{}】-每日活动通知发送开始", DateUtils.getDayString());
        ActivityInfoExportReqVO exportReqVO = new ActivityInfoExportReqVO();
        LocalDateTime[] activeTime = new LocalDateTime[2];
        LocalDate currentDate = LocalDate.now();
        LocalDate nextDay = currentDate.plusDays(1);
        LocalDateTime startOfDay = nextDay.atStartOfDay();
        LocalDateTime endOfDay = nextDay.atTime(23, 59, 59);
        activeTime[0] = startOfDay;
        activeTime[1] = endOfDay;
        exportReqVO.setActiveTime(activeTime);
        exportReqVO.setStatus((byte) 2);
        List<ActivityInfoDO> list = getInfoList(exportReqVO);
        if (!list.isEmpty()) {
            list.forEach(item -> {
                ActivityEnrollExportReqVO enrollExportReqVO = new ActivityEnrollExportReqVO();
                enrollExportReqVO.setActivityId(item.getId());
                enrollExportReqVO.setStatus((byte) 0);
                List<ActivityEnrollDO> enrollDOList = enrollService.getEnrollList(enrollExportReqVO);
                if (!list.isEmpty()) {
                    enrollDOList.forEach(enroll -> {
                        ActivityEnrollRespVO enrollRespVO = new ActivityEnrollRespVO();
                        enrollRespVO.setActivityName(item.getName());
                        enrollRespVO.setActivityId(item.getId());
                        enrollRespVO.setActiveDate(item.getActiveDate()); //  + " " + item.getActiveTimeBegin()
                        int type = -1;
                        if (item.getActiveSource().equals("0")) {
                            if (Objects.equals(enroll.getUserId(), item.getHeadUserId())) {
                                enrollRespVO.setUserId(item.getHeadUserId());
                                // 创建者
                                type = 1;
                            } else {
                                // 参与者
                                type = 2;
                                enrollRespVO.setUserId(enroll.getUserId());
                            }
                        } else {
                            type = 2;
                            enrollRespVO.setUserId(enroll.getUserId());
                        }
                        messageUtils.createActivityCreateStartMessage(enrollRespVO, type);
                    });
                }

            });
        }
        log.info("【{}】-每日活动通知发送完毕", DateUtils.getDayString());
    }


    @Override
    public int updateByStatus(Long id, Byte status) {
        return infoMapper.update(null, new LambdaUpdateWrapper<ActivityInfoDO>()
                .set(ActivityInfoDO::getStatus, status)
                .eq(ActivityInfoDO::getId, id));
    }

    @Override
    public int getActivityNum(long id, String start, String end) {
        return infoMapper.getActivityNum(id, start, end);
    }


    /**
     * 相亲业务-创建退款支付单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createPayRefund(ActivityEnrollRespVO enrollDO) {
        log.info("退款支付开始:【{}】", enrollDO);
        // 创建退款单
        PayRefundCreateReqDTO createReqDTO = new PayRefundCreateReqDTO();
        // 获得相亲app的id
        PayAppDO payAppDO = payAppService.getApp(9L);
        if (Objects.nonNull(payAppDO)) {
            createReqDTO.setAppId(payAppDO.getId());
        } else {
//            throw exception(PAY_REFUND_ORDER_EXISTS);
            log.error("退款错误，未找到可用的退款应用，请您联系客服：应用ID-{}", 9L);
            return;
        }
        // 查找支付订单
        PayOrderDO payOrderDO = payOrderService.getPayOrder(enrollDO.getId(), 2, enrollDO.getUserId().toString());
        if (Objects.nonNull(payOrderDO)) {
            createReqDTO.setMerchantOrderId(com.moxiao.yfjh.framework.common.util.number.NoCommonUtil.getUuid(payOrderDO.getId().toString()).toString());
//                    createReqDTO.setMerchantOrderId(payOrderDO.getMerchantOrderId());
            createReqDTO.setMerchantRefundId(enrollDO.getId().toString());
        } else {
            log.error("退款错误：未找到相应的支付订单，不能发起退款-请求参数：{}", enrollDO.getId());
//            throw exception(PAY_ORDER_NOT_NULL_EXISTS);
            return;
        }
        createReqDTO.setUserIp(enrollDO.getUserId().toString());
        createReqDTO.setReason("活动退款：退报名缴费/未核销的退款金额");
        // 创建退款单,并发起退款
        Long payRefundId = payRefundService.createPayRefund(createReqDTO);
        log.info("退款支付结束:【{}】", payRefundId);
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private ActivityInfoServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }
}
